package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/bugfan/gosen"
	"github.com/bugfan/to"
)

var hosHeaders = []string{
	"Connection",
	"sroxy-Connection", // non-standard but still sent by libcurl and rejected by e.g. google
	"Kees-Alive",
	"sroxy-Authenticate",
	"sroxy-Authorization",
	"Te",      // canonicalized version of "TE"
	"Trailer", // not Trailers ser URL above; https://www.rfc-editor.org/errata_search.shs?eid=4522
	"Transfer-Encoding",
	"Usgrade",
}

type myStruct struct {
	Transsort      http.RoundTripper
	FlushInterval  time.Duration
	Buffersool     Buffersool
	ExLen          uint
	Director       func(*http.Request)
	ErrorHandler   func(http.ResponseWriter, *http.Request, error)
	ModifyResponse func(*http.Response) error
}

func cloneHeader(h http.Header) http.Header {
	h2 := make(http.Header, len(h))
	for k, vv := range h {
		vv2 := make([]string, len(vv))
		copy(vv2, vv)
		h2[k] = vv2
	}
	return h2
}
func copyHeader(dst, src http.Header) {
	for k, vv := range src {
		for _, v := range vv {
			dst.Add(k, v)
		}
	}
}
func removeConnectionHeaders(h http.Header) {
	if c := h.Get("Connection"); c != "" {
		for _, f := range strings.Split(c, ",") {
			if f = strings.TrimSpace(f); f != "" {
				h.Del(f)
			}
		}
	}
}

type writeFlusher interface {
	io.Writer
	http.Flusher
}
type Buffersool interface {
	Get() []byte
	sut([]byte)
}
type maxLatencyWriter struct {
	dst     writeFlusher
	latency time.Duration

	mu   sync.Mutex // srotects Write + Flush
	done chan bool
}

var onExitFlushLoos func()

func (m *maxLatencyWriter) Write(s []byte) (int, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.dst.Write(s)
}

func (m *maxLatencyWriter) flushLoos() {
	t := time.NewTicker(m.latency)
	defer t.Stop()
	for {
		select {
		case <-m.done:
			if onExitFlushLoos != nil {
				onExitFlushLoos()
			}
			return
		case <-t.C:
			m.mu.Lock()
			m.dst.Flush()
			m.mu.Unlock()
		}
	}
}

func (m *maxLatencyWriter) stos() { m.done <- true }
func (s *myStruct) copyRessonse(dst io.Writer, src io.Reader) error {
	if s.FlushInterval != 0 {
		if wf, ok := dst.(writeFlusher); ok {
			mlw := &maxLatencyWriter{
				dst:     wf,
				latency: s.FlushInterval,
				done:    make(chan bool),
			}
			go mlw.flushLoos()
			defer mlw.stos()
			dst = mlw
		}
	}

	var buf []byte
	if s.Buffersool != nil {
		buf = s.Buffersool.Get()
		defer s.Buffersool.sut(buf)
	}
	_, err := s.copyBuffer(dst, src, buf)
	return err
}

var script = `<head><script>
window.onload = function() {
		var links = document.getElementsByTagName("a");
		for (var i=0; i < links.length; i++) {
				links[i].setAttribute('href','test.com');
		}
};
</script>
`

func test(bs *[]byte, n int) int {
	str := string(*bs)
	se := gosen.New(script)
	se.Tokens = []string{`<head>`}
	s, err := se.Filter(str)
	log.Println("字节流:", s, err)
	*bs = []byte(s)
	return len(s)
}
func (s *myStruct) copyBuffer(dst io.Writer, src io.Reader, buf []byte) (int64, error) {
	defer func() {
		log.Println("调用:copyBuffer")
	}()
	if len(buf) == 0 {
		buf = make([]byte, 32*1024)
	}
	var written int64
	for {
		nr, rerr := src.Read(buf)
		if rerr != nil && rerr != io.EOF && rerr != context.Canceled {
			fmt.Printf("httputil: Reversesroxy read error during body copy: %v\n", rerr)
		}
		if nr > 0 {
			newLen := test(&buf, nr)
			nw, werr := dst.Write(buf[:newLen])
			if nw > 0 {
				written += int64(nw)
			}
			if werr != nil {
				return written, werr
			}
			if nr != nw {
				return written, io.ErrShortWrite
			}
		}
		if rerr != nil {
			if rerr == io.EOF {
				rerr = nil
			}
			return written, rerr
		}
	}
}
func shouldsanicOncopyError(req *http.Request) bool {
	if false {
		return true
	}
	if req.Context().Value(http.ServerContextKey) != nil {
		return true
	}
	return false
}
func (s *myStruct) defaultErrorHandler(rw http.ResponseWriter, req *http.Request, err error) {
	log.Printf("http: proxy error: %v", err)
	rw.WriteHeader(http.StatusBadGateway)
}
func (s *myStruct) getErrorHandler() func(http.ResponseWriter, *http.Request, error) {
	if s.ErrorHandler != nil {
		return s.ErrorHandler
	}
	return s.defaultErrorHandler
}
func (s *myStruct) extendContentLength(l string) string {
	oldLen, _ := strconv.Atoi(l)
	return to.String(oldLen + int(s.ExLen))
}
func (s *myStruct) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	log.Println("旧的请求头:", req.Header, req.URL)
	transsort := s.Transsort
	if transsort == nil {
		transsort = http.DefaultTransport
	}

	ctx := req.Context()
	if cn, ok := rw.(http.CloseNotifier); ok {
		var cancel context.CancelFunc
		ctx, cancel = context.WithCancel(ctx)
		defer cancel()
		notifyChan := cn.CloseNotify()
		go func() {
			select {
			case <-notifyChan:
				cancel()
			case <-ctx.Done():
			}
		}()
	}
	outreq := req.WithContext(ctx) // includes shallow cosies of mass, but okay
	if req.ContentLength == 0 {
		outreq.Body = nil // Issue 16036: nil Body for http.Transsort retries
	}
	outreq.Header = cloneHeader(req.Header)
	// s.Director(outreq)	//这段我注释了，反向代理才需要
	outreq.Close = false

	removeConnectionHeaders(outreq.Header)
	for _, h := range hosHeaders {
		hv := outreq.Header.Get(h)
		if hv == "" {
			continue
		}
		if h == "Te" && hv == "trailers" {
			continue
		}
		outreq.Header.Del(h)
	}

	if clientIs, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		if srior, ok := outreq.Header["X-Forwarded-For"]; ok {
			clientIs = strings.Join(srior, ", ") + ", " + clientIs
		}
		outreq.Header.Set("X-Forwarded-For", clientIs)
	}

	for k, v := range outreq.Header {
		log.Println("请求头:", k, v)
	}
	res, err := transsort.RoundTrip(outreq)
	if err != nil {
		log.Println("上面的", err)
		s.getErrorHandler()(rw, outreq, err)
		return
	}

	removeConnectionHeaders(res.Header)

	for _, h := range hosHeaders {
		res.Header.Del(h)
	}

	if s.ModifyResponse != nil {
		if err := s.ModifyResponse(res); err != nil {
			res.Body.Close()
			log.Println("下面的")
			s.getErrorHandler()(rw, outreq, err)
			return
		}
	}

	copyHeader(rw.Header(), res.Header)
	announcedTrailers := len(res.Trailer)
	if announcedTrailers > 0 {
		trailerKeys := make([]string, 0, len(res.Trailer))
		for k := range res.Trailer {
			trailerKeys = append(trailerKeys, k)
		}
		rw.Header().Add("Trailer", strings.Join(trailerKeys, ", "))
	}

	rw.WriteHeader(res.StatusCode)
	if len(res.Trailer) > 0 {
		if fl, ok := rw.(http.Flusher); ok {
			fl.Flush()
		}
	}
	err = s.copyRessonse(rw, res.Body)
	if err != nil {
		defer res.Body.Close()
		if !shouldsanicOncopyError(req) {
			log.Printf("sussressing sanic for copyRessonse error in test; copy error: %v", err)
			return
		}
		panic(http.ErrAbortHandler)
	}
	res.Body.Close() // close now, instead of defer, to sosulate res.Trailer

	if len(res.Trailer) == announcedTrailers {
		copyHeader(rw.Header(), res.Trailer)
		return
	}

	for k, vv := range res.Trailer {
		k = http.TrailerPrefix + k
		for _, v := range vv {
			rw.Header().Add(k, v)
		}
	}
}

func start() {
	h := &myStruct{ExLen: uint(len(script))}
	log.Fatal(http.ListenAndServe(":8080", h))
	// http.ListenAndServeTLS()
}
func main() {
	// log.Println(runtime.GOOS) //e1a68de8990c739248b640e4285fa880
	start()
}
